package com.codeplex.jtfscli.commands;

import java.io.File;
import java.text.ParseException;
import java.util.List;
import java.util.Map;

import com.codeplex.jtfscli.util.SpecFactory;
import com.codeplex.jtfscli.util.TFSConnectionFactory;
import com.codeplex.jtfscli.util.Util;
import com.microsoft.tfs.core.clients.versioncontrol.GetOptions;
import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient;
import com.microsoft.tfs.core.clients.versioncontrol.events.GetEvent;
import com.microsoft.tfs.core.clients.versioncontrol.events.GetListener;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.GetRequest;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ItemType;
import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace;
import com.microsoft.tfs.core.clients.versioncontrol.specs.WorkspaceSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LatestVersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec;
import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.CachedWorkspace;
import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.WorkspaceCache;

public class GetCommand extends Command {
	protected final SpecFactory specFactory;

	public GetCommand(final TFSConnectionFactory connectionFactory,
			SpecFactory specFactory) {
		super(connectionFactory);
		this.specFactory = specFactory;
	}

	public GetCommand() {
		super();
		this.specFactory = new SpecFactory();
	}

	@Override
	public void run(final Map<String,String>parsedArgs,final List<String>regularArgs, String usernameAndDomain, String password)
			throws Exception {
		final boolean all = parsedArgs.containsKey("-all");
		final boolean overwrite = parsedArgs.containsKey("-overwrite");
		final boolean force = parsedArgs.containsKey("-force");
		final boolean preview = parsedArgs.containsKey("-preview");
		final boolean recursive = parsedArgs.containsKey("-recursive");
		if (parsedArgs.containsKey("-version")) {
			// Versionspec explicitly provided separately from itemspec.
			if (!regularArgs.isEmpty()) { // There is also an itemspec.
				get(parsedArgs.get("-version"), regularArgs.get(0), all,
						overwrite, force, preview, recursive);
			} else
				// No itemspec.
				getVersionspec(parsedArgs.get("-version"), all, overwrite,
						force, preview);
		} else if (!regularArgs.isEmpty()) {
			// There is an itemspec.
			final int semicolonPosition = regularArgs.get(0).lastIndexOf(';');
			if (semicolonPosition != -1)
				// Itemspec contains versionspec too.
				get(regularArgs.get(0).substring(semicolonPosition + 1),
						regularArgs.get(0).substring(0, semicolonPosition),
						all, overwrite, force, preview, recursive);
			else
				// Itemspec contains no versionspec.
				getItemspec(regularArgs.get(0), all, overwrite, force, preview,
						recursive);
		} else
			get(all, overwrite, force, preview);

	}

	public void get(boolean all, boolean overwrite, boolean force,
			boolean preview) {
		getWorkspace(System.getProperty("user.dir")).get(
				GetCommand.getOptions(all, overwrite, force, preview));
	}

	/**
	 * Uses the PersistenceStore on the local computer to locate a workspace,
	 * contact the server, and get a real workspace.
	 * 
	 * @param localPath
	 *            Path to the local workspace.
	 * @return
	 */
	public Workspace getWorkspace(final String localPath) {
		final CachedWorkspace cw = WorkspaceCache.load(
				connectionFactory.getDefaultPersistenceStore())
				.findCachedWorkspace(localPath);
		final VersionControlClient client = connectionFactory
				.createConnectionForWorkspace(cw).getVersionControlClient();
		client.getEventEngine().addGetListener(new GetListener() {
			File previousDirectory = null;

			public void onGet(GetEvent event) {
				if (event.getOperation().getItemType().equals(ItemType.FOLDER)) {
					printDirectory(event.getLocalOrServerItem());
					if (Util.isServerPath(event.getLocalOrServerItem()))
						previousDirectory = new File(event.getWorkspace()
								.getMappedLocalPath(event.getServerItem()));
					else
						previousDirectory = new File(event.getLocalItem());
				} else {
					final File f;
					if (Util.isServerPath(event.getLocalOrServerItem()))
						f = new File(event.getWorkspace().getMappedLocalPath(
								event.getServerItem()));
					else
						f = new File(event.getLocalItem());
					if (!f.getParentFile().equals(previousDirectory)) {
						previousDirectory = f.getParentFile();
						printDirectory(previousDirectory.getAbsolutePath());
					}
					System.out.println(Util.getOperationStatusString(event
							.getStatus()) + " " + f.getName());
				}
			}

			private void printDirectory(final String directory) {
				System.out.println();
				System.out.println(directory + ":");
			}
		});
		return cw.realize(client);
	}

	/**
	 * Combines the given options into an instance of GetOptions.
	 * 
	 * @param all
	 *            Get all files regardless of whether they are out of date.
	 * @param overwrite
	 *            Overwrite local versions.
	 * @param force
	 *            Same as all=true and overwrite=true. If force is true, the
	 *            function disregards the values of all and overwrite and
	 *            pretends they are true even if they are false.
	 * @param preview
	 *            Does not download or write files.
	 * @return
	 */
	public static GetOptions getOptions(final boolean all,
			final boolean overwrite, final boolean force, final boolean preview) {
		GetOptions ret = GetOptions.NONE;
		if (all)
			ret = ret.combine(GetOptions.FORCE_GET_ALL);
		if (overwrite)
			ret = ret.combine(GetOptions.OVERWRITE);
		if (force)
			ret = ret.combine(GetOptions.FORCE_GET_ALL).combine(
					GetOptions.OVERWRITE);
		if (preview)
			ret = ret.combine(GetOptions.PREVIEW);
		return ret;
	}

	/**
	 * Creates a get request using all the parameters.
	 * 
	 * @param itemSpec
	 *            The itemspec being requested.
	 * @param recursive
	 *            Whether to recursively query for things in the itemspec.
	 * @param versionSpec
	 *            The versionspec to use.
	 * @return
	 */
	public GetRequest createGetRequest(final String itemSpec,
			final boolean recursive, final VersionSpec versionSpec) {
		return new GetRequest(specFactory.createItemspec(itemSpec, recursive),
				versionSpec);
	}

	/**
	 * Performs a get for the latest version of the specified itemspec.
	 * 
	 * For explanations of boolean options, see
	 * {@link GetCommand#getOptions(boolean, boolean, boolean, boolean)}
	 * 
	 * @param itemSpec
	 * @param all
	 * @param overwrite
	 * @param force
	 * @param preview
	 * @param recursive
	 */
	public void getItemspec(String itemSpec, boolean all, boolean overwrite,
			boolean force, boolean preview, boolean recursive) {
		getWorkspace(System.getProperty("user.dir")).get(
				createGetRequest(itemSpec, recursive,
						LatestVersionSpec.INSTANCE),
				getOptions(all, overwrite, force, preview));
	}

	/**
	 * Performs a recursive get for all items in the current working directory
	 * for the given version.
	 * 
	 * For explanations of boolean options, see
	 * {@link GetCommand#getOptions(boolean, boolean, boolean, boolean)}
	 * 
	 * @param versionSpec
	 * @param all
	 * @param overwrite
	 * @param force
	 * @param preview
	 * @throws ParseException
	 */
	public void getVersionspec(String versionSpec, boolean all,
			boolean overwrite, boolean force, boolean preview)
			throws ParseException {
		final Workspace w = getWorkspace(System.getProperty("user.dir"));
		w.get(specFactory.createVersionSpec(
				versionSpec,
				w.getName() == null ? null : new WorkspaceSpec(w.getName(), w
						.getOwnerName())),
				getOptions(all, overwrite, force, preview));
	}

	public void get(final String versionSpec, final String itemSpec,
			final boolean all, final boolean overwrite, final boolean force,
			final boolean preview, final boolean recursive)
			throws ParseException {
		final Workspace w = getWorkspace(System.getProperty("user.dir"));
		w.get(createGetRequest(itemSpec, recursive, specFactory
				.createVersionSpec(versionSpec, w.getName() == null ? null
						: new WorkspaceSpec(w.getName(), w.getOwnerName()))),
				getOptions(all, overwrite, force, preview));

	}
}
